<?php

namespace app\api\model;

use app\admin\model\vip\Code;
use app\exceptions\ApiException;
use ba\Exception;
use EasyWeChat\Factory;
use think\facade\Db;
use think\Model;

/**
 * Banner
 */
class Order
{
    // 表名
    protected $distance = 30 * 1000;

    public function scenicOrder($params)
    {
        $scenic_id = $params['scenic_id'] ?? 0;
        $member_id = $params['member_id'] ?? 0;
        $order_time = $params['order_time'] ?? '';
        if (!$scenic_id || !$order_time) {
            throw new ApiException('缺少参数');
        }

        //判断是否符合当前的时间
        $config = get_sys_config('','system');
        $currentTime = date('H:i:s'); // 获取当前时间的小时和分钟，格式为 H:i:s
        $currentTimestamp = strtotime($currentTime);

        $earliestTimestamp = strtotime($config['earliest']);
        $latestTimestamp = strtotime($config['Latest']);
        if ($currentTimestamp <= $earliestTimestamp || $currentTimestamp >= $latestTimestamp) {
            throw new ApiException('请于'.$config['earliest']." - ".$config['Latest'].'之间预约');
        }


        //获取用户信息
        $member = (new Member())->where(['id' => $member_id])->find();
        //判断是否已认证
        if (!$member['username'] || !$member['id_number']) {
            throw new ApiException('请先实名认证');
        }

        //查询最近住宿的时间
        $lastOrder = (new ScenicOrder())->getLastOrder($member_id);
        if ($lastOrder) {
            throw new ApiException('您有订单正在进行，请勿重复操作');
        }

        //查询景区信息
        $scenic = (new Scenic())->where(['id' => $scenic_id, 'status' => 1])->find();
        if (!$scenic) {
            throw new ApiException('景区不存在或已下架');
        }

        if($scenic['is_lock'] == 1) throw new ApiException($scenic['lock_reason'] ?? '当日景区预约已满员');
        //查询当天已经预约的数量
        $todayOrder = (new ScenicOrder())->getTodayOrderNums(1);
        if($todayOrder == $scenic['limit_nums'] && $scenic['limit_nums'] != 0) throw new ApiException('当日景区预约已满员');


        $fiveOrder = (new ScenicOrder())->getTodayOrderNums(15);
        if($fiveOrder >= $scenic['fifteen'] && $scenic['fifteen'] != 0) throw new ApiException('景区预约已满员');




        $scenic_vip_card = explode(',', $scenic['vip_card_ids']);
        //查询用户是否开通会员
        $member_vip_card = (new MemberVipCard())->where(['member_id' => $member_id, 'vip_type' => 1, 'status' => 1])->find();

        if (!$member_vip_card) {
            throw new ApiException('请开通会员卡');
        }

        if($member_vip_card['is_freeze'] == 1) throw new ApiException('您的会员卡已被冻结'."\n".'原因：'.$member_vip_card['reason']);

        //查询会员卡信息
        $vip_card = (new VipCard())->where(['id' => $member_vip_card['vip_card_id']])->find();
        if (!$vip_card) {
            throw new ApiException('会员信息错误，请联系管理员');
        }
        if (!in_array($member_vip_card['vip_card_id'], $scenic_vip_card)) {
            throw new ApiException('您的会员类型不支持该景区');
        }
//            if($vip_card['use_type'] == 1 && $member_vip_card['nums'] == 0) throw new ApiException('次数已用完');
        if($vip_card['use_type'] == 1 && $member_vip_card['nums'] <= 0){
            throw new ApiException('您的次数已用完');
        }

        //查询用户是否已经预约过该景区
        $order = (new ScenicOrder())->getUserScenicOrder($member, $member_vip_card, $scenic_id);
        if ($order) {
            throw new ApiException('每张会员卡仅可预约一次该景区');
        }
        $price = $scenic['vip_price'];


        //查询该用户是否已经预约其他景区，获取次数
        $order_nums = (new ScenicOrder())->getOrderNums($member_id);

        $out_trade_no = $this->build_order_no('SO');
        $data = [
            'out_trade_no' => $out_trade_no,
            'admin_id' => $member_vip_card['admin_id'] ?? 0,
            'scenic_id' => $scenic_id,
            'price' => $price,
            'member_id' => $member_id,
            'vip_card_id' => $member_vip_card['vip_card_id'] ?? 0,
            'order_time' => strtotime($order_time),
            'create_time' => time(),
            'nums' => $order_nums + 1,
            'code_number' => $member_vip_card['vip_code']
        ];
        $model = (new ScenicOrder());

        try {
            if ($price > 0) {
                //需要支付
                $data['status'] = 0;
                $model->save($data);
                //去支付
                $title = "预约景区" . $price;
                $config = $this->toPay($title, $out_trade_no, $price, 'scenicCallback', $member['openid']);
                return api_message($config);
            } else {
                Db::startTrans();
                //直接生成订单
                $data['status'] = 1;
                $scenic_id = $model->insertGetId($data);
                if ($member_vip_card && $vip_card['use_type'] == 1) {
                    (new MemberVipCard())->where(['id' => $member_vip_card['id']])->setDec('nums');
                }
                //记录状态值
                (new OrderLog())->addLog(1, $scenic_id, 1, '预约已提交，正在处理中');
                Db::commit();
                return api_message([], '预约成功');
            }
        } catch (\Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }
    }


    public function getHotelOrderList($params)
    {
        $member_id = $params['member_id'] ?? 0;
        $status = $params['status'] ?? 0;
        $map = [];
        $map[] = ['ho.member_id', '=', $member_id];
        $map[] = ['ho.status', '>', 0];
        if($status){
            $map[] = ['ho.status', '=', $status];
        }
        $list = (new HotelOrder())->alias('ho')
            ->join('hotel h', 'h.id = ho.hotel_id')
            ->join('member m', 'm.id = ho.member_id')
            ->where($map)
            ->field(
                'ho.id as order_id,h.cover,h.name,ho.status,m.username,
                ho.order_start_time,ho.order_end_time,h.longitude,h.latitude'
            )
            ->order('ho.id desc')
            ->paginate(10)->toArray();

        if($list['total'] == 0) return  api_message($list);
        foreach ($list['data'] as &$val){
            $val['cover'] = full_url($val['cover']);
            $val['date'] = date("Y-m-d", $val['order_start_time'])." 至 ". date("Y-m-d", $val['order_end_time']);

            unset($val['order_start_time'],$val['order_end_time']);
        }

        return api_message($list);
    }


    public function getScenicOrderList($params){
        $member_id = $params['member_id'] ?? 0;
        $lat = $params['lat'] ?? '';
        $lng = $params['lng'] ?? '';
        $status = $params['status'] ?? 0;
        $map = [];
        $map[] = ['so.member_id', '=', $member_id];
        $map[] = ['so.status', '>', 0];
        if($status){
            $map[] = ['so.status', '=', $status];
        }
        $list = (new ScenicOrder())->alias('so')
            ->join('scenic s', 's.id = so.scenic_id')
            ->join('member m', 'm.id = so.member_id')
            ->where($map)
            ->field(
                'so.id as order_id,s.cover,s.name,so.status,m.username,
                so.order_time,s.longitude,s.latitude,so.qrcode,so.is_qrcode,so.url'
            )
            ->order('so.id desc')
            ->paginate(10)->toArray();

        if($list['total'] == 0) return  api_message($list);
        foreach ($list['data'] as &$val){
            $distance = $this->calculateDistance($lat, $lng, $val['latitude'], $val['longitude']);
            if($distance > $this->distance && !in_array($val['status'],[3,4])){
                $val['qrcode'] = '';
            }else{
                $val['qrcode'] = $val['qrcode'] ? full_url($val['qrcode']) : '';
            }
            $val['distance'] = $distance;
            $val['cover'] = full_url($val['cover']);
            $val['date'] = date("Y-m-d", $val['order_time']);

            unset($val['order_time']);
        }

        return api_message($list);
    }


    public function getHotelOrderDetail($params){
        $order_id = $params['order_id'] ?? 0;
        $lat = $params['lat'] ?? '';
        $lng = $params['lng'] ?? '';

        $order = (new HotelOrder())->alias('ho')
            ->join('hotel h', 'h.id = ho.hotel_id')
            ->join('member m', 'm.id = ho.member_id')
            ->where(['ho.id' => $order_id])
            ->field(
                'ho.id as order_id,h.cover,h.name,ho.status,m.username,m.id_number,m.phone as user_phone,h.address,h.phone,hotel_cate_ids,
                ho.order_start_time,ho.order_end_time,h.longitude,h.latitude,hotel_des,traffic_bus,book_notice,
                ho.room_num,ho.room_password,ho.notes,ho.desc'
            )->find();
        $order['cate_name'] = (new HotelCate())->where('id','in',$order['hotel_cate_ids'])->column('name');
        $order['distance'] = (new Scenic())->calculateDistance($lat, $lng, $order['latitude'], $order['longitude']);
        $order['cover'] = full_url($order['cover']);

        //查询状态详细信息
        $order['status_text'] = (new OrderLog())->getStatusText($order_id,'2');

        //判断是否在距离内
        $distance = $this->calculateDistance($lat, $lng, $order['latitude'], $order['longitude']);
        $order['is_show'] = false;
        if($distance < $this->distance && in_array($order['status'],[2,3])) {
            $order['is_show'] = true;
        }else{
            unset($order['username'], $order['id_number'], $order['phone'],$order['room_num'],$order['room_password'],$order['desc']);
        }

       return api_message($order);
    }


    public function getScenicOrderDetail($params){
        $order_id = $params['order_id'] ?? 0;
        $lat = $params['lat'] ?? '';
        $lng = $params['lng'] ?? '';

        $order = (new ScenicOrder())->alias('so')
            ->join('scenic s', 's.id = so.scenic_id')
            ->join('cate c','c.id = s.cate_id')
            ->join('member m', 'm.id = so.member_id')
            ->where(['so.id' => $order_id])
            ->field(
                'so.id as order_id,s.cover,s.name,c.name as cate_name,
                so.status,m.username,m.id_number,m.phone as user_phone,s.address,s.openTime,s.longitude,
                s.latitude,so.qrcode,so.notes,so.desc,so.is_qrcode,so.url'
            )->find();

        if(!$order) return api_message();
        $order['distance'] = (new Scenic())->calculateDistance($lat, $lng, $order['latitude'], $order['longitude']);
        $order['cover'] = full_url($order['cover']);
        $order['qrcode'] = $order['qrcode'] ? full_url($order['qrcode']) : '';

        //查询状态详细信息
        $order['status_text'] = (new OrderLog())->getStatusText($order_id,1);

        //判断是否在距离内
        $distance = $this->calculateDistance($lat, $lng, $order['latitude'], $order['longitude']);
        $order['is_show'] = false;
        if($distance < $this->distance && in_array($order['status'],[2,3,4])) {
            $order['is_show'] =true;
        }else{
            unset($order['username'], $order['id_number'], $order['user_phone'],$order['desc']);
        }
        return api_message($order);
    }





    public function hotelOrder($params)
    {
        $hotel_id = $params['hotel_id'] ?? 0;
        $member_id = $params['member_id'] ?? 0;
        $order_start_time = $params['order_start_time'] ?? '';
        $order_end_time = $params['order_end_time'] ?? '';
        if (!$hotel_id || !$order_end_time || !$order_end_time) {
            throw new ApiException('缺少参数');
        }

        //判断是否符合当前的时间
        $config = get_sys_config('','system');
        $currentTime = date('H:i:s'); // 获取当前时间的小时和分钟，格式为 H:i:s
        $currentTimestamp = strtotime($currentTime);

        $earliestTimestamp = strtotime($config['earliest']);
        $latestTimestamp = strtotime($config['Latest']);
        if ($currentTimestamp <= $earliestTimestamp || $currentTimestamp >= $latestTimestamp) {
            throw new ApiException('请于'.$config['earliest']." - ".$config['Latest'].'之间预约');
        }

        //获取用户信息
        $member = (new Member())->where(['id' => $member_id])->find();
        if (!$member['username'] || !$member['id_number']) {
            throw new ApiException('请先实名认证');
        }

        //查询该用户是否为会员
        $member_vip_card = (new MemberVipCard())->where(['member_id' => $member_id, 'status' => 1, 'vip_type' => 2]
        )->find();
        if (!$member_vip_card) {
            throw new ApiException('请先开通会员卡');
        }
        if($member_vip_card['is_freeze'] == 1) throw new ApiException('您的会员卡已被冻结'."\n".'原因：'.$member_vip_card['reason']);

        //查询会员卡信息
        $vip_card = (new VipCard())->where(['id' => $member_vip_card['vip_card_id']])->find();
        if (!$vip_card) {
            throw new ApiException('会员信息错误，请联系管理员');
        }

        if($vip_card['use_type'] == 1 && $member_vip_card['nums'] <= 0){
            throw new ApiException('您的次数已用完');
        }

        //查询民宿信息
        $hotel = (new Hotel())->where(['id' => $hotel_id, 'status' => 1])->find();
        if (!$hotel) {
            throw new ApiException('民宿不存在或已下架');
        }

        //判断该会员是否 符合民宿
        if (!in_array($member_vip_card['vip_card_id'], $hotel['vip_card_ids'])) {
            throw new ApiException('该民宿不支持您的会员类型');
        }

        $order = (new HotelOrder())->getUserHotelOrder($member, $member_vip_card, $hotel_id);
        if ($order) {
            throw new ApiException('每张会员卡仅可预约一次该民宿');
        }

        //查询最近住宿的时间
        $lastOrder = (new HotelOrder())->getLastOrder($member_id);
        if ($lastOrder) {
            throw new ApiException('您有订单正在进行，请勿重复操作');
        }
        //查询该用户是否已经预约其他景区，获取次数
        $order_nums = (new HotelOrder())->getOrderNums($member_id);

        $out_trade_no = $this->build_order_no('HO');
        $data = [
            'out_trade_no' => $out_trade_no,
            'admin_id' => $member_vip_card['admin_id'],
            'hotel_member_id' => $hotel['member_id'],
            'hotel_id' => $hotel_id,
            'member_id' => $member_id,
            'vip_card_id' => $member_vip_card['vip_card_id'],
            'order_start_time' => strtotime($order_start_time),
            'order_end_time' => strtotime($order_end_time),
            'create_time' => time(),
            'nums' => $order_nums + 1,
            'code_number' => $member_vip_card['vip_code']
        ];


        //判断该时间是否需要支付
        $hotel_price = (new HotelPrice())->where(['hotel_id' => $hotel_id, 'date' => $order_start_time])->find();
        try {
            if ($hotel_price) {
                (new HotelOrder())->insert($data);
                //去支付
                $title = "预约民宿" . $hotel_price['price'];
                $config = $this->toPay(
                    $title,
                    $out_trade_no,
                    $hotel_price['price'],
                    'hotelCallback',
                    $member['openid']
                );
                return api_message($config);
            } else {
                Db::startTrans();
                //保存数据
                $data['status'] = 1;
                $hotel_id = (new HotelOrder())->insertGetId($data);
                //扣除次数
                if ($vip_card['use_type'] == 1) {
                    (new MemberVipCard())->where(['id' => $member_vip_card['id']])->setDec('nums');
                }
                //添加记录
                (new OrderLog())->addLog(2, $hotel_id, 1, '预约已提交，正在处理中');
                Db::commit();
                return api_message('预约成功');
            }
        } catch (\Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }
    }


    public function build_order_no($prefix)
    {
        return $prefix . date('ymd') . substr(
                implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))),
                0,
                8
            );
    }


    public function toPay($body, $out_trade_no, $pay_amount, $callback, $openid)
    {
        $pay_amount = 0.01;
        $app = Factory::payment(config('wechat.wechat_pay'));
        $jssdk = $app->jssdk;
        $data = [
            'body' => $body,
            'out_trade_no' => $out_trade_no,
            'total_fee' => $pay_amount * 100,
            'notify_url' => env('domain.api_url') . '/index.php/call/pay/' . $callback, // 支付结果通知网址，如果不设置则会使用配置里的默认地址
            'trade_type' => 'JSAPI', // 请对应换成你的支付方式对应的值类型
            'openid' => $openid, //
        ];
        \think\facade\Log::info(json_encode($data));
        $result = $app->order->unify($data);
        if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
            $config = $jssdk->sdkConfig($result['prepay_id']); // 返回数组
            if (isset($config['timestamp'])) { // 检查timestamp是否存在
                $config['timeStamp'] = $config['timestamp']; // 复制timestamp的值到timeStamp
                unset($config['timestamp']); // 删除timestamp键
            }
            return $config;
        } else {
            throw new ApiException($result['err_code_des']);
        }
    }


    public function canelOrder($params)
    {
        $type = $params['type'] ?? 1;
        $order_id = $params['order_id'] ?? 0;

        try {
            Db::startTrans();
            if ($type == 1) {
                //景区
                $order = (new ScenicOrder())->where(['id' => $order_id])->find();
                if(!$order) throw new ApiException("订单不存在");
                if ($order['status'] != 1) {
                    throw new ApiException('该订单暂时无法取消');
                }
                (new ScenicOrder())->where(['id' => $order_id])->update(['status' => 6]);
            } else {
                //民宿
                $order = (new HotelOrder())->where(['id' => $order_id])->find();
                if(!$order) throw new ApiException("订单不存在");
                if ($order['status'] != 1) {
                    throw new ApiException('该订单暂时无法取消');
                }
                (new HotelOrder())->where(['id' => $order_id])->update(['status' => 6]);
                //添加记录
            }
            (new OrderLog())->addLog($type, $order_id, 6, '用户已取消');
            //添加记录
            if($order['price'] > 0){
                (new Pay())->refund($order['transaction_id'],$order['out_trade_no'],$order['price'],$order['price']);
            }
            Db::commit();
            return api_message([], '取消成功');
        }catch (\Exception $e){
            Db::rollback();
            throw new ApiException($e->getMessage());
        }
    }



    public function autoHotelOrder($params){
        $lat = $params['lat'] ?? '';
        $lng = $params['lng'] ?? '';
        $member_id = $params['member_id'] ??  0;

        $order = (new HotelOrder())->alias('ho')
            ->join('hotel h','ho.hotel_id = h.id')
            ->where(['ho.status' => 2,'ho.member_id' => $member_id])
            ->field('ho.id as order_id,h.latitude,h.longitude')
            ->select();
        if(count($order) == 0)  return  api_message();
        try {
            Db::startTrans();
            foreach ($order as $val){
                $distance = $this->calculateDistance($lat,$lng,$val['latitude'],$val['longitude']);
                if($distance < $this->distance){
                    (new HotelOrder())->where(['id' => $val['order_id']])->update(['status' => 3]);
                    (new OrderLog())->addLog(2, $val['order_id'], 3, "用户已到店");
                }
            }
            Db::commit();
        }catch (\Exception $e) {
            Db::rollback();
        }

        return api_message();
    }


    public function calculateDistance($lat1, $lon1, $lat2, $lon2)
    {
        // 地球半径，单位千米
        $earthRadius = 6371.0;

        // 转换为弧度
        $lat1 = deg2rad($lat1);
        $lon1 = deg2rad($lon1);
        $lat2 = deg2rad($lat2);
        $lon2 = deg2rad($lon2);

        // 计算经纬度差值
        $deltaLat = $lat2 - $lat1;
        $deltaLon = $lon2 - $lon1;

        // 哈弗辛公式
        $a = sin($deltaLat / 2) * sin($deltaLat / 2) +
            cos($lat1) * cos($lat2) * sin($deltaLon / 2) * sin($deltaLon / 2);
        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));

        // 计算距离
        $distanceKm = $earthRadius * $c;

        // 如果距离小于1000米，返回米，否则返回公里
        return round($distanceKm * 1000);

    }

}
